﻿using System;
using System.Drawing;

using OpenTK;
using OpenTK.Graphics.OpenGL;

using AnimatingHair.Entity;
using AnimatingHair.Auxiliary;

namespace AnimatingHair.Rendering
{
    class Renderer : IRenderable
    {
        private Camera camera;
        private Light light;
        private Light backLight;
        private HairRenderer hairRenderer;
        private BustRenderer bustRenderer;
        private double angle = -1.5;

        public Renderer( Camera camera, Scene scene )
        {
            this.camera = camera;

            light = new Light
            {
                Intensity = 0.6,
                Position = new Vector3d( 5, 0, 5 )
            };

            backLight = new Light
            {
                Intensity = 0.7,
                Position = new Vector3d( 5, 0, 5 )
            };

            //initializeOpenGL();

            hairRenderer = new HairRenderer( camera, scene.Hair );
            bustRenderer = new BustRenderer( scene.Bust );
        }

        public void Initialize()
        {
            initializeOpenGL();
        }

        private void initializeOpenGL()
        {
            GL.ClearColor( Color.CornflowerBlue );

            GL.Enable( EnableCap.Texture2D );
            GL.TexEnv( TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (float)TextureEnvMode.Modulate );

            GL.Enable( EnableCap.Blend );
            GL.BlendFunc( BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha );

            GL.Enable( EnableCap.Lighting );
            GL.ShadeModel( ShadingModel.Smooth );

            GL.Enable( EnableCap.Light0 );
            GL.Light( LightName.Light0, LightParameter.Diffuse, new float[] { (float)light.Intensity, (float)light.Intensity, (float)light.Intensity } );
            GL.Light( LightName.Light0, LightParameter.Ambient, new float[] { 0.1f, 0.1f, 0.1f } );
            //GL.Light( LightName.Light0, LightParameter.Specular, new float[] { 0.1f, 0.1f, 0.1f } );

            GL.Enable( EnableCap.Light1 );
            GL.Light( LightName.Light1, LightParameter.Diffuse, new float[] { (float)backLight.Intensity, (float)backLight.Intensity, (float)backLight.Intensity } );
            GL.Light( LightName.Light1, LightParameter.Specular, new float[] { 0.1f, 0.1f, 0.1f } );
        }

        public void Render()
        {
            prepareBufferAndMatrix();

            angle += 0.005;

            light.Position = new Vector3d( 10 * Math.Sin( angle ), 2 * Math.Cos( angle ), 10 * Math.Cos( angle ) );
            GL.Light( LightName.Light0, LightParameter.Position, new Vector4( (Vector3)light.Position, 1 ) );

            Vector3d camDir = camera.Target - camera.Eye;
            Vector3d right = Vector3d.Cross( camDir, camera.Up );
            right.Normalize();
            backLight.Position = camera.Eye + 2 * camDir + 10 * camera.Up + 10 * right;
            GL.Light( LightName.Light1, LightParameter.Position, new Vector4( (Vector3)backLight.Position, 1 ) );

            GL.Enable( EnableCap.DepthTest );
            GL.Disable( EnableCap.Texture2D );
            drawAxes();
            bustRenderer.Render();
            light.Render();

            GL.DepthMask( false );
            GL.Enable( EnableCap.Texture2D );
            hairRenderer.Render();

            GL.DepthMask( true );
        }

        private void prepareBufferAndMatrix()
        {
            GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );

            Matrix4 modelview = Matrix4.LookAt( (Vector3)camera.Eye, (Vector3)camera.Target, (Vector3)camera.Up );
            GL.MatrixMode( MatrixMode.Modelview );
            GL.LoadMatrix( ref modelview );
        }

        private static void drawAxes()
        {
            GL.Color3( Color.Red );
            GL.Begin( BeginMode.Lines );
            {
                GL.Vertex3( -Vector3.UnitX );
                GL.Vertex3( Vector3.UnitX );
                GL.Vertex3( -Vector3.UnitY );
                GL.Vertex3( Vector3.UnitY );
                GL.Vertex3( -Vector3.UnitZ );
                GL.Vertex3( Vector3.UnitZ );
            }
            GL.End();
        }
    }
}